package com.jweb.core;

import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.jweb.annotaion.JAutowired;
import com.jweb.annotaion.JController;
import com.jweb.annotaion.JTask;
import com.jweb.annotaion.JTransaction;
import com.jweb.annotaion.JValue;
import com.jweb.config.JWebConfig;
import com.jweb.config.Plugins;
import com.jweb.config.def.CharSet;
import com.jweb.config.def.DefWebConfig;
import com.jweb.core.handle.Controller;
import com.jweb.core.handle.Handle;
import com.jweb.core.inter.HandleInterceptor;
import com.jweb.core.inter.JWebInterceptor;
import com.jweb.plugin.jdbc.JdbcTemplate;
import com.jweb.plugin.mybatis.proxy.AopMybatisMapperHandler;
import com.jweb.plugin.mybatis.proxy.AopMybatisTransactionHandler;
import com.jweb.plugin.quartz.QuartzPlugin;
import com.jweb.plugin.quartz.dto.CronTriggerDto;
import com.jweb.plugin.quartz.dto.JobDetailDto;
import com.jweb.plugin.quartz.dto.SchedulerDto;
import com.jweb.util.ClassUtil;
import com.jweb.util.FormatUtil;

/**
 * jweb控制中心
 * 
 * @author Yuan
 */
public class JWebFilter implements Filter {
	private JWebConfig jwebConfig;
	private static Handle handle = new Handle();
	// ==公共资源======
	/** scan_AllClass：遍历项目所有class,只遍历不实例。 */
	private static final Set<Class<?>> scan_AllClass = new LinkedHashSet<Class<?>>();
	/** controllerClass:所有控制类,未实例 */
	private static final Map<String, Class<? extends Controller>> controllerClass = new HashMap<String, Class<? extends Controller>>();
	/** controllerBean:所有控制类,已实例 */
	private static final Map<String, Object> controllerBean = new HashMap<String, Object>();
	/** constants:所有常量 */
	private static final Map<String, String> constants = new HashMap<String, String>();
	/** plugins:所有插件 */
	private static final Plugins plugins = new Plugins();
	/** allHandleInter:请求拦截类,已实例 */
	private static final Set<HandleInterceptor> allHandleInter = new LinkedHashSet<HandleInterceptor>();
	/** allJWebInter:项目拦截类,已实例 */
	private static final Set<JWebInterceptor> allJWebInter = new LinkedHashSet<JWebInterceptor>();
	// ==公共资源======
	private static Logger log;
	private static int contextPathLength;
	private static JdbcTemplate jdbctemplate;
	private int jsp_buffer_size = 0;// 8192;
	private List<SchedulerDto> listScheduler = new ArrayList<SchedulerDto>();
	private final String quartz = "org.quartz.Job";
	private static volatile AtomicInteger concurrentNo = new AtomicInteger(0); // 网站并发数

	private List<String> ignoreURIs = new ArrayList<String>();
	private List<String> ignoreExts = new ArrayList<String>();

	@Override
	public void init(FilterConfig cfg) throws ServletException {
		init(cfg.getInitParameter("configClass"), cfg.getInitParameter("propertyFile"), cfg.getServletContext().getContextPath());
		ignoresURL(cfg,null);
	}

	private boolean ignoresURL(FilterConfig cfg, String req_uri) {
		if (cfg != null) {
			// 某些URL前缀不予处理（例如 /img/***）
			String ignores = cfg.getInitParameter("ignore");
			if (ignores != null)
				for (String ig : ignores.split(","))
					ignoreURIs.add(ig.trim());

			// 某些URL扩展名不予处理（例如 *.jpg）
			ignores = cfg.getInitParameter("ignoreExts");
			if (ignores != null)
				for (String ig : ignores.split(","))
					ignoreExts.add('.' + ig.trim());
			
		}
		if (req_uri != null) {
			if(FormatUtil.isEmpty(req_uri) || req_uri.equals("/"))
				return true;
			// 过滤URL前缀
			for (String ignoreURI : ignoreURIs)
				if (req_uri.startsWith(ignoreURI))
					return true;
			// 过滤URL后缀
			for (String ignoreExt : ignoreExts)
				if (req_uri.endsWith(ignoreExt))
					return true;
		}
		
		return false;
	}

	public void init(String configClass, String propertyFile, String contextPath) {
		long startTime = System.currentTimeMillis();
		try {
			log = LoggerFactory.getLogger(JWebFilter.class);
			log.info("==0,加载日志完成。");
			createConfig(configClass); // 1.实例化
			long cutT = System.currentTimeMillis();
			initDefaultConstants(); // 加载默认常量[1]
			jwebConfig.loadPropertyFile(propertyFile, constants);// 加载配置文件及常量[2]
			jwebConfig.configConstant(constants); // 加载用户常量[3]
			jsp_buffer_size = FormatUtil.toInt(constants.get("jsp.buffer.size"));
			scan_AllClass.addAll(new ClassUtil<Class<?>>().getClasses("", ".class"));// 常量加载完后扫描类，必须在这里
			log.info("==1,加载常量完成。\t\t\t[{}ms]", new Object[] { (System.currentTimeMillis() - cutT) });
			cutT = System.currentTimeMillis();
			jwebConfig.configPlugin(plugins); // 加载插件
			log.info("==2,加载插件完成。\t\t\t[{}ms]", new Object[] { (System.currentTimeMillis() - cutT) });
			cutT = System.currentTimeMillis();
			initPackClass();// 扫描类，后期可加多种注解类
			log.info("==3,自动注入类完成。\t\t\t[" + (System.currentTimeMillis() - cutT) + "ms]");
			contextPathLength = (contextPath == null || "/".equals(contextPath) ? 0 : contextPath.length());
			cutT = System.currentTimeMillis();
			initJWebInter(1);// 加载系统拦截
			initQuartz();// 加载Quartz作业
			log.info("==4,加载附加操作。\t\t\t[{}ms]", new Object[] { (System.currentTimeMillis() - cutT) });

			log.info("===============初始化完成==============");
		} catch (Exception e) {
			e.printStackTrace();
			log.info("===============初始化失败==============");
			System.exit(0);
		}
		log.info("==项目加载耗时{}ms", new Object[] { System.currentTimeMillis() - startTime });
	}

	@Override
	public void doFilter(ServletRequest req, ServletResponse rep, FilterChain chain) throws IOException, ServletException {
		HttpServletRequest request = (HttpServletRequest) req;
		HttpServletResponse response = (HttpServletResponse) rep;
		// request.setCharacterEncoding(constants.get("encoding"));//在这里设置编码是不明智的，要用户自己控制
		if (jsp_buffer_size > 0)
			response.setBufferSize(jsp_buffer_size);
		String target = request.getRequestURI();
		if(ignoresURL(null, target.substring(contextPathLength))){
			chain.doFilter(request, response);
			return;
		}
		//======================================
		boolean[] isHandled = { false };
		try {
			concurrentNo.incrementAndGet();
			handle.handle(target, request, response, isHandled);
		} catch (Exception e) {
			log.error(request.getRequestURI() + "请求异常:" + e.getMessage());
			e.printStackTrace();
		}finally{concurrentNo.decrementAndGet();};
		if (isHandled[0] == false)
			chain.doFilter(request, response);
		
		System.out.println("并发压力：" + concurrentNo.get());
	}

	@Override
	public void destroy() {
		log.info("==项目正在终止。。。");
		scan_AllClass.clear();
		controllerClass.clear();
		controllerBean.clear();
		constants.clear();
		initJWebInter(2);
	}

	/**
	 * 初始化jwebConfig
	 * 
	 * @throws ClassNotFoundException
	 * @throws IllegalAccessException
	 * @throws InstantiationException
	 */
	private void createConfig(String configClass) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
		if (configClass == null) {
			log.error("[警告]配置类<param-name>configClass</param-name>未在web.xml中设置初始化。");
			jwebConfig = new DefWebConfig();
			return;
		}
		Object temp = Class.forName(configClass).newInstance();
		if (temp instanceof JWebConfig) {
			jwebConfig = (JWebConfig) temp;
		} else
			throw new RuntimeException("web.xml中初始化参数类型异常：" + configClass);
	}

	/**
	 * bean 注入
	 * 
	 * @throws Exception
	 */
	@SuppressWarnings("unchecked")
	private void initPackClass() throws Exception {
		for (Class<?> cla : scan_AllClass) {
			if (cla.getGenericSuperclass() != null) {// 有父类
				// 1、遍历 注解(树状 ，以JController为头)Action入口
				JController jc = cla.getAnnotation(JController.class);
				if (jc != null) {
					controllerClass.put(jc.value(), (Class<? extends Controller>) cla); // 普通类Class,未实例
					controllerBean.put(jc.value(), startEach(cla)); // 动态代理类，已实例
					log.info("==Controller:  [" + jc.value() + "]" + cla.getName());
				}
				// 2、遍历父类为HandleInterceptor的bean
				else if (SuperclassEquals(cla, HandleInterceptor.class.getName())) {
					allHandleInter.add((HandleInterceptor) startEach(cla)); // 遍历Handle拦截
					log.info("==HandleInterceptor:  " + cla.getName());
				}
				// 3、遍历父类为JWebInterceptor的bean
				else if (SuperclassEquals(cla, JWebInterceptor.class.getName())) {
					allJWebInter.add((JWebInterceptor) startEach(cla)); // 遍历项目拦截
					log.info("==JWebInterceptor:  " + cla.getName());
				}
				// 4、加载Quartz
				else if (InterfaceEquals(cla, quartz)) {
					JTask jt = cla.getAnnotation(JTask.class);
					if (jt != null) {
						SchedulerDto dto = new SchedulerDto();
						dto.setJobdetail(new JobDetailDto(jt.jobName(), jt.jobGroup(), cla));
						dto.setTrigger(new CronTriggerDto(jt.triggerName(), jt.triggerGroup(), jt.cron()));
						listScheduler.add(dto);
						log.info("==Quartz:  [" + jt.cron() + "]" + cla.getName());
					}
				}
			} else {
				// =======
			}
		}
		// tempBean.clear();
	}

	/**
	 * 初始化默认常量，，后续会有越来越多配置加入常量<br>
	 * 可被配置文件覆盖
	 */
	private void initDefaultConstants() {
		constants.put("encoding", CharSet.Default); // 默认编码
		constants.put("ViewPrefix", "/WEB-INF/jsp/"); // 视图地址
		constants.put("ViewSuffix", ".jsp"); // 视图类型
	}

	private void initQuartz() {
		try {
			if (listScheduler.size() > 0) {
				Class.forName(quartz);
				QuartzPlugin quartzPlugin = new QuartzPlugin(listScheduler);
				quartzPlugin.start();
				plugins.add(quartzPlugin);
			} else {
				listScheduler = null;
			}
		} catch (Exception e) {
			log.error("未引入Quartz.jar包，不能执行定时任务。");
		}
	}

	/** 执行所有JWebInterceptor拦截 */
	private void initJWebInter(int type) {
		for (JWebInterceptor j : JWebFilter.allJWebInter)
			if (type == 1)
				j.init();
			else
				j.destroy();
	}

	private Object startEach(Class<?> cla) throws Exception {
		return eachServer(cla, false, new String[] {});
	}

	/**
	 * 遍历注解@JAutowired的服务层和接口层<br/>
	 * tempBean:临时bean,不用重复的去生成bean
	 */
	private static Map<String, Object> tempBean = new HashMap<String, Object>();
	private List<String> curBean = new LinkedList<String>();

	private Object eachServer(Class<?> cla, boolean isService, Object[] params) throws Exception {
		if (curBean.contains(cla.getName()))
			throw new Exception("注入发生 循环依赖：" + cla.getName());
		curBean.add(cla.getName());
		Object bean = null;
		if (params.length == 0)
			bean = tempBean.get(cla.getName());
		if (bean != null) {
			curBean.remove(cla.getName());
			return bean;
		}
		if (isService)
			bean = getTransaction(cla);
		if (bean == null) {
			bean = cla.newInstance();
		}
		List<Field[]> listFs = new ArrayList<Field[]>();
		listFs.add(cla.getDeclaredFields());
		listFs.add(cla.getSuperclass().getDeclaredFields());
		String[] mapperDao = (System.getProperty("mapperPackage") + "").split(";");
		for (Field[] fs : listFs) {
			for (Field f : fs) {
				f.setAccessible(true);// 取消java语言访问检查以访问private变量
				JAutowired ja = f.getAnnotation(JAutowired.class);
				if (ja != null) {
					if (f.getType().getSimpleName().equals("List")) {// 针对List<抽象类>
						ParameterizedType pt = (ParameterizedType) f.getGenericType();
						Class<?> ptC = ((Class<?>) pt.getActualTypeArguments()[0]);
						List<Object> tempPtc = new ArrayList<Object>();
						for (Class<?> ac : scan_AllClass) {
							if (ac.getSuperclass() == ptC) {
								tempPtc.add(eachServer(ac, true, new String[] {}));
								continue;
							}
							Class<?>[] aci = ac.getInterfaces();
							for (Class<?> acii : aci) {
								if (ptC == acii && !ac.getName().equals(ptC.getName())) {
									tempPtc.add(eachServer(ac, true, new String[] {}));
								}
							}
						}
						f.set(bean, tempPtc);
					} else {
						// 1+512+1024
						List<Integer> modifiers = Arrays.asList(new Integer[] { 1025, 1537 });
						if (modifiers.contains(f.getType().getModifiers())) {// 针对接口、抽象类
							if (!ja.value().equals(Class.class)) {
								f.set(bean, eachServer(ja.value(), true, params));
							} else
								f.set(bean, getInterface(f.getType(), mapperDao));
						} else { // 针对普通类
							f.set(bean, eachServer(f.getType(), true, params));
						}
					}
				}
				JValue jv = f.getAnnotation(JValue.class);
				if (jv != null) {
					String key = jv.value();
					String type = f.getType().getName();
					String typem = "java.lang.String";
					if (typem.equals(type)) {
						if (key.indexOf("${") == 0 && key.indexOf("}") == key.length() - 1)
							key = key.substring(2, key.length() - 1);
						String value = FormatUtil.toString(getConstants().get(key), null);
						if (value == null)
							throw new NullPointerException("[" + key + "]为空");
						f.set(bean, value);
					} else {
						throw new Exception("@JValue(\"" + key + "\")注入失败,无法将[" + typem + "]转为：[" + type + "]");
					}
				}
			}
		}
		tempBean.put(cla.getName(), bean);
		curBean.remove(cla.getName());
		return bean;
	}

	/**
	 * JDK动态代理Mapper,针对于Mybatis弄的
	 * 
	 * @throws Exception
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	private Object getInterface(Class<?> cla, String[] mapperDao) throws Exception {
		boolean isDao = false;
		for (String str : mapperDao) {
			if (cla.getName().indexOf(str) >= 0) {
				isDao = true;
				break;
			}
		}
		if (isDao) {
			AopMybatisMapperHandler ah = new AopMybatisMapperHandler();
			Object intface = ah.init(cla);
			return intface;
		} else {
			Object iico = null;
			int matchingSize = 0;
			for (Class<?> ac : scan_AllClass) {
				Class<?>[] aci = ac.getInterfaces();
				for (Class<?> acii : aci) {
					if (cla == acii && !ac.getName().equals(cla.getName())) {
						++matchingSize;
						if (matchingSize > 1)
							throw new Exception("Interface 实现类发现多个，请指定一个！" + cla.getName());
						iico = eachServer(ac, true, new String[] {});
					}
				}
			}
			if (iico == null)
				throw new Exception("Interface 实现类未找到，请指定一个！" + cla.getName());
			return iico;
		}
	}

	/** Cglib动态代理Mapper,针对于Mybatis 事务弄的 */
	private boolean Mtx = true;

	private Object getTransaction(Class<?> cla) throws InstantiationException, IllegalAccessException {
		if (Mtx) {
			try {
				Class.forName("net.sf.cglib.proxy.MethodInterceptor");
				try {
					int count = 0;
					for (Method me : cla.getMethods())
						if (me.getAnnotation(JTransaction.class) != null)
							++count;
					if (count == 0)
						return null;
					log.info(cla.getName() + "已开启mybatis事物机制。");
					AopMybatisTransactionHandler ah = new AopMybatisTransactionHandler();
					Object obj = ah.getInstance(cla.newInstance());
					return obj;
				} catch (Exception e) {
					e.printStackTrace();
					return null;
				}
			} catch (ClassNotFoundException e) {
				Mtx = false;
				log.error("当前项目未使用Cglib动态代理。无法完成事务操作。");
				return cla.newInstance();
			}
		} else {
			return null;
		}
	}

	private boolean SuperclassEquals(Class<?> cla, String superName) {
		return cla.getSuperclass().getName().equals(superName);
	}

	private boolean InterfaceEquals(Class<?> cla, String interName) {
		Class<?> inter[] = cla.getInterfaces();
		if (inter.length == 0)
			return false;
		return inter[0].getName().equals(interName);
	}

	// ==对外披露=====================================================
	public static int getContextPathLength() {
		return contextPathLength;
	}

	public static void setJdbctemplate(JdbcTemplate jdbctemplate) {
		JWebFilter.jdbctemplate = jdbctemplate;
	}

	public static JdbcTemplate getJdbctemplate() {
		return jdbctemplate;
	}

	/** constants:所有常量 */
	public static Map<String, String> getConstants() {
		return constants;
	}

	public static String getConstants(String key) {
		return constants.get(key);
	}

	public static Map<String, Class<? extends Controller>> getControllerclass() {
		return controllerClass;
	}

	public static Set<HandleInterceptor> getAllhandleinter() {
		return allHandleInter;
	}

	public static Map<String, Object> getControllerbean() {
		return controllerBean;
	}

	public static Set<Class<?>> getScanAllclass() {
		return scan_AllClass;
	}

	@SuppressWarnings("unchecked")
	public static <T> T getBean(Class<T> clas) {
		return (T) tempBean.get(clas.getName());
	}

	public static void main(String[] args) {

	}
}
